#include "RenderTarget.h"
#include "Buffer.h"
#include "Color.h"
#include "RenderTexture.h"
#include "NeroCore\NMemoryManager.h"

using namespace Graphics;

RenderTarget::RenderTarget(void)
	:mVertexBuffer(NULL),
	 mIndexBuffer(NULL),
	 mVertexCount(0),
	 mIndexCount(0),
	 mScreenHeight(0),
	 mScreenWidth(0),
	 mBitmapHeight(0),
	 mBitmapWidth(0),
	 mPreviousPosX(0),
	 mPreviousPosY(0),
	 mInstanceCount(0)
{
}

RenderTarget::~RenderTarget(void)
{
}

bool RenderTarget::Initialize(int screenWidth, int screenHeight, int bitmapWidth, int bitmapHeight)
{
	// Store the screen size.
	mScreenWidth = screenWidth;
	mScreenHeight = screenHeight;

	// Store the size in pixels that this bitmap should be rendered at.
	mBitmapWidth = bitmapWidth;
	mBitmapHeight = bitmapHeight;

	// Initialize the previous rendering position to negative one.
	mPreviousPosX = -1;
	mPreviousPosY = -1;

	// Initialize the vertex and index buffers.
	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	bool result = InitializeBuffers(nDevice);

	mRenderTexture = MY_NEW(Memory::HID_Rendering, "Render Target Texture") RenderTexture();
	mRenderTexture->Initialize(screenWidth,screenHeight);

	return result;
}

void RenderTarget::Deinitialize()
{
	DeinitializeBuffers();
}

bool RenderTarget::Render(int positionX, int positionY)
{
	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	// Re-build the dynamic vertex buffer for rendering to possibly a different location on the screen.
	bool result = UpdateBuffers(nDevice, positionX, positionY);

	// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
	RenderBuffers(nDevice);

	return result;
}

int RenderTarget::GetIndexCount()
{
	return mIndexCount;
}

bool RenderTarget::InitializeBuffers(D3DDevice* device)
{
	// Set the number of vertices in the vertex array.
	mVertexCount = 6;

	// Set the number of indices in the index array.
	mIndexCount = mVertexCount;

	// Create the vertex array.
	VertexType* vertices = MY_NEW(Memory::HID_Rendering, "Render Target Vertices") VertexType[mVertexCount];
	
	// Create the index array.
	unsigned long* indices = MY_NEW(Memory::HID_Rendering, "Render Target Indicies") unsigned long[mIndexCount];
	
	// Initialize vertex array to zeros at first.
	memset(vertices, 0, (sizeof(VertexType) * mVertexCount));

	// Load the index array with data.
	for(int i = 0; i < mIndexCount; i++)
	{
		indices[i] = i;
	}

	// Set up the description of the static vertex buffer.
	D3D11_BUFFER_DESC vertexBufferDesc;
	vertexBufferDesc.Usage					= D3D11_USAGE_DYNAMIC;
	vertexBufferDesc.ByteWidth				= sizeof(VertexType) * mVertexCount;
	vertexBufferDesc.BindFlags				= D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags			= D3D11_CPU_ACCESS_WRITE;
	vertexBufferDesc.MiscFlags				= 0;
	vertexBufferDesc.StructureByteStride	= 0;

	// Give the subresource structure a pointer to the vertex data.
	D3D11_SUBRESOURCE_DATA vertexData;
	vertexData.pSysMem			= vertices;
	vertexData.SysMemPitch		= 0;
	vertexData.SysMemSlicePitch = 0;

	// Now create the vertex buffer.
	mVertexBuffer = MY_NEW(Memory::HID_Rendering,"Render Target VBuffer") Buffer();
	mVertexBuffer->Initialize(vertexBufferDesc, vertexData);

	// Set up the description of the static index buffer.
	D3D11_BUFFER_DESC indexBufferDesc;
	indexBufferDesc.Usage				= D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth			= sizeof(unsigned long) * mIndexCount;
	indexBufferDesc.BindFlags			= D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags		= 0;
	indexBufferDesc.MiscFlags			= 0;
	indexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the index data.
	D3D11_SUBRESOURCE_DATA indexData;
	indexData.pSysMem			= indices;
	indexData.SysMemPitch		= 0;
	indexData.SysMemSlicePitch	= 0;

	// Create the index buffer.
	mIndexBuffer = MY_NEW(Memory::HID_Rendering,"Render Target VBuffer") Buffer();
	mIndexBuffer->Initialize(indexBufferDesc, indexData);

	// Release the arrays now that the vertex and index buffers have been created and loaded.
	MY_DELETE_ARRAY(vertices);
	vertices = NULL;

	MY_DELETE_ARRAY(indices);
	indices = NULL;

	mInstanceCount = 1;

	InstanceType* instances = MY_NEW(Memory::HID_Rendering, "Instances") InstanceType[mInstanceCount];
	instances[0].Position = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

	mInstanceBuffer = MY_NEW(Memory::HID_Rendering, "Instance Buffer") Buffer();

	// Set up the description of the instance buffer.
	D3D11_BUFFER_DESC  instanceBufferDesc;
	instanceBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	instanceBufferDesc.ByteWidth = sizeof(InstanceType) * mInstanceCount;
	instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	instanceBufferDesc.CPUAccessFlags = 0;
	instanceBufferDesc.MiscFlags = 0;
	instanceBufferDesc.StructureByteStride = 0;
	
	D3D11_SUBRESOURCE_DATA instanceData;
	instanceData.pSysMem = instances;
	instanceData.SysMemPitch = 0;
	instanceData.SysMemSlicePitch = 0;

	mInstanceBuffer->Initialize(instanceBufferDesc, instanceData);

	return true;
}

void RenderTarget::DeinitializeBuffers()
{
	// Release the index buffer.
	if(mIndexBuffer)
	{
		MY_DELETE(mIndexBuffer);
		mIndexBuffer = NULL;
	}

	// Release the vertex buffer.
	if(mVertexBuffer)
	{
		MY_DELETE(mVertexBuffer);
		mVertexBuffer = NULL;
	}

	// Release the instance buffer.
	if(mInstanceBuffer)
	{
		mInstanceBuffer->Release();
		mInstanceBuffer = NULL;
	}
}

bool RenderTarget::UpdateBuffers(D3DDevice* device, int positionX, int positionY)
{
	// If the position we are rendering this bitmap to has not changed then don't update the vertex buffer since it
	// currently has the correct parameters.
	if((positionX == mPreviousPosX) && (positionY == mPreviousPosY))
	{
		return true;
	}
	
	// If it has changed then update the position it is being rendered to.
	mPreviousPosX = positionX;
	mPreviousPosY = positionY;

	// Calculate the screen coordinates of the left side of the bitmap.
	float left = (float)((mScreenWidth / 2) * -1) + (float)positionX;

	// Calculate the screen coordinates of the right side of the bitmap.
	float right = left + (float)mBitmapWidth;

	// Calculate the screen coordinates of the top of the bitmap.
	float top = (float)(mScreenHeight / 2) - (float)positionY;

	// Calculate the screen coordinates of the bottom of the bitmap.
	float bottom = top - (float)mBitmapHeight;

	// Create the vertex array.
	VertexType* vertices = MY_NEW(Memory::HID_Rendering,"Render Target VertexType") VertexType[mVertexCount];

	// Load the vertex array with data.
	// First triangle.
	vertices[0].Position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
	vertices[0].Color = Colors::Green;
	vertices[0].Texture = D3DXVECTOR2(0.0f, 0.0f);
	vertices[0].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	vertices[1].Position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
	vertices[1].Color = Colors::Green;
	vertices[1].Texture = D3DXVECTOR2(1.0f, 1.0f);
	vertices[1].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	vertices[2].Position = D3DXVECTOR3(left, bottom, 0.0f);  // Bottom left.
	vertices[2].Color = Colors::Green;
	vertices[2].Texture = D3DXVECTOR2(0.0f, 1.0f);
	vertices[2].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	// Second triangle.
	vertices[3].Position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
	vertices[3].Color = Colors::Green;
	vertices[3].Texture = D3DXVECTOR2(0.0f, 0.0f);
	vertices[3].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	vertices[4].Position = D3DXVECTOR3(right, top, 0.0f);  // Top right.
	vertices[4].Color = Colors::Green;
	vertices[4].Texture = D3DXVECTOR2(1.0f, 0.0f);
	vertices[4].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	vertices[5].Position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
	vertices[5].Color = Colors::Green;
	vertices[5].Texture = D3DXVECTOR2(1.0f, 1.0f);
	vertices[5].Normal = D3DXVECTOR3(0.0f, 0.0f, -1.0f);

	// Lock the vertex buffer so it can be written to.
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	HRESULT result = device->GetDeviceContext()->Map(mVertexBuffer->GetResource(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	CHECK(result);

	// Get a pointer to the data in the vertex buffer.
	VertexType* verticesPtr = (VertexType*)mappedResource.pData;

	// Copy the data into the vertex buffer.
	memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * mVertexCount));

	// Unlock the vertex buffer.
	device->GetDeviceContext()->Unmap(mVertexBuffer->GetResource(), 0);

	// Release the vertex array as it is no longer needed.
	MY_DELETE_ARRAY(vertices);
	vertices = NULL;

	return true;
}

void RenderTarget::RenderBuffers(D3DDevice* device)
{
	unsigned int stride[2];
	unsigned int offset[2];

	ID3D11Buffer* bufferPointers[2];
	// Set vertex buffer stride and offset.
	stride[0] = sizeof(VertexType); 
	stride[1] = sizeof(InstanceType);

	offset[0] = 0;
	offset[1] = 0;

	bufferPointers[0] = mVertexBuffer->GetResource();
	bufferPointers[1] = mInstanceBuffer->GetResource();
    
	// Set the vertex buffer to active in the input assembler so it can be rendered.
	device->GetDeviceContext()->IASetVertexBuffers(0, 2, bufferPointers, stride, offset);

	// Set the index buffer to active in the input assembler so it can be rendered.
	ID3D11Buffer* ibuffer = mIndexBuffer->GetResource();
	device->GetDeviceContext()->IASetIndexBuffer(ibuffer, DXGI_FORMAT_R32_UINT, 0);

	// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
	device->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

RenderTexture* RenderTarget::GetTexture()
{
	return mRenderTexture;
}

void RenderTarget::SetAsTarget()
{
	mRenderTexture->SetRenderTarget();
}